package com.googlecode.cswish.view;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.List;

import org.apache.struts2.StrutsException;
import org.apache.struts2.components.Component;

import com.googlecode.cswish.util.FreeMarkerHelper;
import com.opensymphony.xwork2.util.ValueStack;

import freemarker.core.Environment;
import freemarker.core.TemplateElement;
import freemarker.core.TextBlock;

/**
 * It's used to extend a default page behavior
 * 
 * immediate == true ==> Run the content and wait the 'Mark' tag to call it
 * immediate == false ==> wait the 'Mark' tag call and run it
 * 
 * Example:<br>
 * <br>
 * <@s.extends name="" alias=""/><br> 
 * <br>
 * <==><br>
 * <br>
 * Class <i>name</i> extends <i>alias</i><br>
 * <br>
 * @author Jerry.Feng Date: 2008-6-2
 * @see com.googlecode.cswish.view.Mark, com.googlecode.cswish.view.Include, <#include>
 */
public class Extends extends Component {

    public Extends(ValueStack stack) {
		super(stack);
	}
    
    protected String target;
    
    protected String name;
    
    /**
     * Execute the block immediate.
     * It's available only when 'name' isn't overridden. 
     */
    protected boolean immediate;

    public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public String getTarget() {
		return target;
	}

	public void setTarget(String target) {
		this.target = target;
	}
	
	public boolean isImmediate() {
		return immediate;
	}

	public void setImmediate(boolean immediate) {
		this.immediate = immediate;
	}

	public boolean start(Writer writer) {
		Object targetObject = Mark.getExtension(stack, target);
		
		TemplateElement nestedBlock = null;
		Environment environment = null;
		
		// get the nested block
		if (name != null || targetObject == null) {	// the 'if' is only for improving performance
			environment = Environment.getCurrentEnvironment();
			List<TemplateElement> elementStack = FreeMarkerHelper.getEnvElementStack(environment);
			TemplateElement element = (TemplateElement) elementStack.get(elementStack.size() - 1);
			nestedBlock = FreeMarkerHelper.getEnvNestedBlock(element);
			if (nestedBlock == null) {
				nestedBlock = new TextBlock("");
			}
		}
		
		// cache it
		if (name != null) {
			Mark.getExtensions(stack).put(name, nestedBlock);
		}
		
		String page = (String) stack.getContext().get(Include.INCLUDE_PAGE);
		// override 'name' when 'name' is null
		if (targetObject == null) {
			if (immediate) {
				Writer body = new StringWriter();
				FreeMarkerHelper.execute(environment, nestedBlock, body);
				try {
					body.close();
				} catch (IOException e) {
		            throw new StrutsException("IOError while writing the body: " + e.getMessage(), e);
		        }
				
				Mark.addExtensionFor(stack, target, page, body.toString());
			} else {
				Mark.addExtensionFor(stack, target, page, nestedBlock);
			}
		} else {
			Mark.addExtendsionPage(stack, target, page);
		}
		return false;
    }
}